﻿using System.IO;
using System.Security.Cryptography;

namespace com.lythen.file.Security
{
    /***************************************************************************
     * CLR版本：4.0.30319.42000
     *唯一标识：a9224f52-f49c-4f44-b330-127f69fa75c7
     *  创建人：赖仁良
     *创建时间：2023/12/19 17:05:17
     *功能描述：
     * ----------------------------------------------------------------
     * 修改人：
     * 时间：
     * 修改说明：
     *=========================================================================*/
    /// <summary>
    /// 
    /// </summary>
    public class FileSecurity
    {
        static SMSecurity sMSecurity = SMSecurity.Instance;
        const string passKey = "U/QazwSDxnUNp3l19";
        /// <summary>
        /// 对指定目录进行压缩，并签名
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="saveFileName"></param>
        /// <returns></returns>
        public static string ZipAndSignFile(string folder)
        {
            // 压缩文件
            DirectoryInfo directoryInfo = new DirectoryInfo(folder);
            string zipFileName = Path.Combine(directoryInfo.Parent.FullName, directoryInfo.Name + ".zip");
            if (File.Exists(zipFileName))
            {
                File.Delete(zipFileName);
            }
            string encFileName = Path.Combine(directoryInfo.Parent.FullName, directoryInfo.Name + ".geof");
            // 加密
            EncryptFile(zipFileName, encFileName);
            File.Delete(zipFileName);

            return encFileName;
        }
        /// <summary>
        /// 对指定文件进行验签和解压
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public static string VerifyAndUnZipFile(string fileName, string folder)
        {
            string save_folder = Path.Combine(folder, Path.GetFileNameWithoutExtension(fileName));
            if (!Directory.Exists(save_folder))
            {
                Directory.CreateDirectory(save_folder);
            }
            string zipFileName = Path.Combine(folder, Path.GetFileNameWithoutExtension(fileName) + ".zip");
            DecryptFile(fileName, zipFileName);


            return save_folder;
        }
        /// <summary>
        /// 文件加密，使用AES流加密，AES密码和IV使用SM2公钥加密，存放在文件头。
        /// </summary>
        /// <param name="inputFile"></param>
        /// <param name="outputFile"></param>
        public static void EncryptFile(string inputFile, string outputFile)
        {
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.GenerateKey();
                aesAlg.GenerateIV();

                // Encrypt the AES key and IV with RSA
                byte[] encryptedKey = sMSecurity.SM2EncryptBytesByPublicKey(aesAlg.Key);
                byte[] encryptedIV = sMSecurity.SM2EncryptBytesByPublicKey(aesAlg.IV);

                using (FileStream inputFileStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
                using (FileStream outputFileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
                using (ICryptoTransform encryptor = aesAlg.CreateEncryptor())
                using (CryptoStream cryptoStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write))
                {
                    // Write encrypted key and IV to the output file
                    outputFileStream.Write(encryptedKey, 0, encryptedKey.Length);
                    outputFileStream.Write(encryptedIV, 0, encryptedIV.Length);

                    // Write the encrypted data to the output file
                    inputFileStream.CopyTo(cryptoStream);
                }
            }
        }
        /// <summary>
        /// 文件解密，先读取文件前段的SM2加密后的密码和IV，使用SM2私钥解密
        /// 使用解密后的密码，使用AES对文件流进行解密
        /// </summary>
        /// <param name="inputFile"></param>
        /// <param name="outputFile"></param>
        public static void DecryptFile(string inputFile, string outputFile)
        {
            using (Aes aesAlg = Aes.Create())
            {
                using (FileStream inputFileStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
                {
                    // Read the encrypted key and IV from the input file
                    // 这里的129和113，是SM2加密后的长度，这个值在方法EncryptFile调试的时候获取，然后在这里设置，一般是固定的。
                    byte[] encryptedKey = new byte[129];
                    byte[] encryptedIV = new byte[113];
                    inputFileStream.Read(encryptedKey, 0, encryptedKey.Length);
                    inputFileStream.Read(encryptedIV, 0, encryptedIV.Length);

                    // Decrypt the key and IV with RSA
                    aesAlg.Key = sMSecurity.SM2DecryptBytesByPrivateKey(encryptedKey);
                    aesAlg.IV = sMSecurity.SM2DecryptBytesByPrivateKey(encryptedIV);

                    using (FileStream outputFileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
                    using (ICryptoTransform decryptor = aesAlg.CreateDecryptor())
                    using (CryptoStream cryptoStream = new CryptoStream(outputFileStream, decryptor, CryptoStreamMode.Write))
                    {
                        // Read the encrypted data from the input file and decrypt it
                        inputFileStream.CopyTo(cryptoStream);
                    }
                }
            }
        }

    }
}
